// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/printer"
	"go/token"
	"os"
	"path/filepath"
	"strings"
)

// godefs返回-godefs模式的输出。
func (p *Package) godefs(f *File) string {
	var buf bytes.Buffer

	fmt.Fprintf(&buf, "// cmd/cgo-godefs生成的代码；不编辑。\n“
	fmt.Fprintf(&buf, "// %s%s\n“，filepath.Base（os.Args[0]），strings.Join（os.Args[1:]，“”）
	fmt.Fprintf(&buf, "\n")

	override := make(map[string]string)

	// 允许源文件指定覆盖映射。
	// 例如，套接字数据结构引用
	// 到in_addr和in_addr6结构，但我们希望
	// 能够将它们视为字节数组，因此godefs
	// 包syscall中的输入
	// None
	// None
	// None
	// None
	for _, g := range f.Comments {
		for _, c := range g.List {
			i := strings.Index(c.Text, "+godefs map")
			if i < 0 {
				continue
			}
			s := strings.TrimSpace(c.Text[i+len("+godefs map"):])
			i = strings.Index(s, " ")
			if i < 0 {
				fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text)
				continue
			}
			override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:])
		}
	}
	for _, n := range f.Name {
		if s := override[n.Go]; s != "" {
			override[n.Mangle] = s
		}
	}

	// 否则，如果源文件显示键入tc.whatever，
	// 用“T”作为C的mangling，
	// 定义中除外（在函数末尾处理）。
	refName := make(map[*ast.Expr]*Name)
	for _, r := range f.Ref {
		refName[r.Expr] = r.Name
	}
	for _, d := range f.AST.Decls {
		d, ok := d.(*ast.GenDecl)
		if !ok || d.Tok != token.TYPE {
			continue
		}
		for _, s := range d.Specs {
			s := s.(*ast.TypeSpec)
			n := refName[&s.Type]
			if n != nil && n.Mangle != "" {
				override[n.Mangle] = s.Name.Name
			}
		}
	}

	// 使用typedefs扩展覆盖：
	// 如果我们知道C.xxx的格式应该是T
	// xxx是yyy的typedef，将C.yyy格式设置为T。
	for typ, def := range typedef {
		if new := override[typ]; new != "" {
			if id, ok := def.Go.(*ast.Ident); ok {
				override[id.Name] = new
			}
		}
	}

	// 应用覆盖。
	for old, new := range override {
		if id := goIdent[old]; id != nil {
			id.Name = new
		}
	}

	// 任何仍然使用_C语法的名称都不会被编译，
	// 虽然总的来说我们不知道他们是否都成功了
	// 进入文件，所以我们不能在这里警告。
	// None
	// 最常见的情况是联合类型，它以
	// _Ctype_union，其中typedef[name]是Go字节
	// 适当大小的数组（例如[4]字节）。
	// 用字节数组替换这些联合类型。
	for name, id := range goIdent {
		if id.Name == name && strings.Contains(name, "_Ctype_union") {
			if def := typedef[name]; def != nil {
				id.Name = gofmt(def)
			}
		}
	}

	conf.Fprint(&buf, fset, f.AST)

	return buf.String()
}

var gofmtBuf bytes.Buffer

// gofmt返回AST节点的gofmt格式字符串。
func gofmt(n interface{}) string {
	gofmtBuf.Reset()
	err := printer.Fprint(&gofmtBuf, fset, n)
	if err != nil {
		return "<" + err.Error() + ">"
	}
	return gofmtBuf.String()
}

// gofmtLineReplacer用于为
// 将AST表达式放在一行上。lexer通常插入一个
// 每个换行符上都有分号，所以我们可以用分号替换换行符。
// 然而，在lexer无法插入的情况下，我们不能这样做
// 分号。我们只需要担心可能在一个特定的时间内发生的情况
// 表达式通过gofmt传递，这意味着复合文本和
// （由于打印机可能因位置而插入换行符）
// （信息）操作员。
var gofmtLineReplacer = strings.NewReplacer(
	// 要替换\n而不替换；从
	// https:
	// 除了++--）]}
	"++\n", "++;",
	"--\n", "--;",

	"+\n", "+ ",
	"-\n", "- ",
	"*\n", "* ",
	"/\n", "/ ",
	"%\n", "% ",
	"&\n", "& ",
	"|\n", "| ",
	"^\n", "^ ",
	"<\n", "< ",
	">\n", "> ",
	"=\n", "= ",
	"!\n", "! ", // 今天在gofmt是不可能的
	"(\n", "(",
	"[\n", "[", // 今天在gofmt是不可能的
	"{\n", "{",
	",\n", ",",
	".\n", ". ",
	":\n", ": ", // 今天在gofmt是不可能的

	"\n", ";",
)

// gofmtLine返回AST节点的gofmt格式字符串，
// 确保它位于一条线上。
func gofmtLine(n interface{}) string {
	return gofmtLineReplacer.Replace(gofmt(n))
}
